<?php
	
	namespace Aspic\Form\Element;
	use Aspic as A;
	use Aspic\Security\Security as S;

	/**
	* Validatable form element with fields validation possibilities
	* 
	* PHP conditions functions takes two args: ($fieldValue, $fieldsArray) (see FormValidation)
	 * @todo Make this a Trait and an interface
	*/
	abstract class AbstractValidatableElement extends AbstractFilterableElement {
		const VALIDATION_TYPE_NORMAL = 'normal';
		const VALIDATION_TYPE_ARRAY  = 'array';
		const VALIDATION_TYPE_FILE   = 'file';
		
		const PHP_VALIDATION = 'php';
		const JS_VALIDATION  = 'js';
		const PJS_VALIDATION = 'pjs';
		const DEFAULT_ERROR_MESSAGE = 'Error on this field';
		
		protected $_phpValidationsRules;
		protected $_jsValidationsRules;
		
		protected $_fieldErrors; // Current errors of the field
		
		public function __construct($name, array $options = array(), array $attributes = array(), array $validationsRules = array(), array $fieldErrors = array()) {
			parent::__construct($name, $options, $attributes);
			
			$this->_phpValidationsRules = array();
			$this->_jsValidationsRules = array();
			
			$this->setValidationRules($validationsRules);
			
			$this->opt('fieldErrors', $fieldErrors, true);
		}
		
		public function addFieldError($errorMessage) {
			$this->_fieldErrors[] = $errorMessage;
		}
		
		/**
		 * Return the errors created by the parent container after call isValid on it
		 * @return Array Errors
		 */
		public function getValidationErrors() {
			return $this->_fieldErrors;
		}
		
		/**
		* Rules must have a key "from" with PHP_VALIDATION, JS_VALIDATION 
		* and "type" with VALIDATION_TYPE_ARRAY, VALIDATION_TYPE_CUSTOM or VALIDATION_TYPE_NORMAL
		*/
		public function setValidationRules(array $rules) {
			$baseRule = array(
				'type' => ValidatableElement::VALIDATION_TYPE_NORMAL, 
				'params' => array(),
				'errorMessage' => static::DEFAULT_ERROR_MESSAGE, 
				'condition' => array('php' => null, 'js' => null), 
			);
			
			foreach($rules as $globalRule) {
				if(!isset($globalRule['from'])) {
					$from = static::PJS_VALIDATION;
				}
				else {
					$from = $globalRule['from'];
					unset($globalRule['from']);					
				}
				
				$rule = array_merge($baseRule, $globalRule);
				
				if($from == static::PHP_VALIDATION OR $from == static::PJS_VALIDATION) {
					$fromRule = $rule;
					$fromRule['condition'] = $rule['condition']['php'];
					
					$this->_phpValidationsRules[] = array_merge($baseRule, $fromRule);
				}
				
				if($from == static::JS_VALIDATION OR $from == static::PJS_VALIDATION) {
					$fromRule = $rule;
					$fromRule['condition'] = $rule['condition']['js'];
					
					$this->_jsValidationsRules[] = array_merge($baseRule, $fromRule);
				}
			}
		}
		
		public function out() {
			$content = $this->_lineStart;
			
			$content .= $this->outElement();
			
			$content .=  $this->_lineEnd;
			
			return $content;
		}

		/**
		* @param int $validateFrom PHP_VALIDATION, JS_VALIDATION or PHP_VALIDATION | JS_VALIDATION
		* @param array $conditions Array with keys 'php' => a php function that must return true to validate the field and a js function (or func name) in 'js' key
		* or directly the function if $validateFrom is not both JS and PHP.
		*/
		public function addValidation($rule, $errorMessage = null, $params = array(), $conditions = null, $validateFrom = self::PJS_VALIDATION) {
			if($rule == 'custom' AND $validateFrom == self::PJS_VALIDATION) {
				throw new A\Exception('Could not set custom validation with the same params for PHP and Javascript validation (call addPhpValidation and addJsValidation instead)');
			}
			
			if($conditions == null) {
				$conditions = array('php' => null, 'js' => null);
			}
			// When not an array and $validateFrom is not both JS and PHP
			elseif(!is_array($conditions) AND ($validateFrom == self::PHP_VALIDATION OR $validateFrom == self::JS_VALIDATION)) {
				if($validateFrom == self::PHP_VALIDATION) {
					$conditions['php'] = $condition;
				}
				else {
					$conditions['js'] = $condition;
				}
			}
		
			if(($validateFrom == static::PHP_VALIDATION OR $validateFrom == static::PJS_VALIDATION) AND !$this->validationExists($rule, self::PHP_VALIDATION)) {
				$this->_phpValidationsRules[] = array(
					'type' => self::VALIDATION_TYPE_NORMAL, 
					'rule' => $rule,
					'params' => $params,
					'errorMessage' => $errorMessage, 
					'condition' => $conditions['php'], 
				);
			}
			
			if(($validateFrom == static::JS_VALIDATION OR $validateFrom == static::PJS_VALIDATION) AND !$this->validationExists($rule, self::JS_VALIDATION)) {
				$this->_jsValidationsRules[] = array(
					'type' => self::VALIDATION_TYPE_NORMAL, 
					'rule' => $rule,
					'params' => $params,
					'errorMessage' => $errorMessage, 
					'condition' => $conditions['js'], 
				);
			}
			
			return $this;
		}
		
		public function addPhpValidation($rule, $errorMessage = null, $params = array(), $conditions = null) {
			return $this->addValidation($rule, $errorMessage, $params, $conditions, self::PHP_VALIDATION);
		}
		
		public function addJsValidation($rule, $errorMessage = null, $params = array(), $conditions = null) {
			return $this->addValidation($rule, $errorMessage, $params, $conditions, self::JS_VALIDATION);
		}
		
		/**
		* Works only on PHP side
		*/
		public function addFileValidation($rule, $errorMessage = null, array $params = array(), $condition = null) {
			$this->_phpValidationsRules[] = array(
				'type' => self::VALIDATION_TYPE_FILE, 
				'rule' => $rule,
				'params' => $params,
				'errorMessage' => $errorMessage, 
				'condition' => $condition,
			);
				
			return $this;
		}

		/**
		* Valid a list of element (elements of a multiple select for example)
		*/
		public function addArrayValidation($rule, $errorMessage = null, $params = array(), $conditions = null) {
			if($conditions == null) {
				$conditions = array('php' => null, 'js' => null);
			}
		
			// if($validateFrom & static::PHP_VALIDATION) {
				$this->_phpValidationsRules[] = array(
					'type' => self::VALIDATION_TYPE_ARRAY, 
					'rule' => $rule,
					'params' => $params,
					'errorMessage' => $errorMessage, 
					'condition' => $conditions['php'], 
				);
			// }
			
			/*
			if($validateFrom & static::JS_VALIDATION) {
				$this->_jsValidationsRules[] = array(
					'type' => self::VALIDATION_TYPE_ARRAY, 
					'rule' => $rule,
					'params' => $params,
					'errorMessage' => $errorMessage, 
					'condition' => $conditions['js'], 
				);
			}
			*/
			
			return $this;
		}
		
		public function hasJsValidationRules() {
			return ((bool)count($this->_jsValidationsRules));
		}
		
		public function hasPhpValidationRules() {
			return ((bool)count($this->_phpValidationsRules));
		}
		
		public function validationExists($rule, $from = self::PJS_VALIDATION) {
			$existsJs = A\UArray::inLinesSearch($this->_jsValidationsRules, $rule, 'rule');
			$existsPhp = A\UArray::inLinesSearch($this->_phpValidationsRules, $rule, 'rule');
			
			if(($from == static::PHP_VALIDATION OR $from == static::PJS_VALIDATION) AND $existsPhp) {
				return true;
			}
			elseif(($from == static::JS_VALIDATION OR $from == static::PJS_VALIDATION) AND $existsJs) {
				return true;
			}
			else {
				return false;
			}
			
		}
		
		/**
		* Remove all validations for rule $rule
		*/
		public function removeValidation($rule) {
			$tmp = array();
		
			foreach($this->opt('phpValidationsRules') as $val) {
				if($val['rule'] != $rule) {
					$tmp[] = $val;
				}
			}
			
			$this->opt('phpValidationsRules', $tmp);
			
			$tmp = array();
		
			foreach($this->opt('jsValidationsRules') as $val) {
				if($val['rule'] != $rule) {
					$tmp[] = $val;
				}
			}
			
			$this->opt('jsValidationsRules', $tmp);
		}
		
		/**
		 * Change the error message of an EXISTING rule
		 * @param type $rule
		 * @param type $errorMessage
		 * @param type $validateFrom Change only for PHP / JS or both
		 */
		public function setErrorMessage($ruleName, $errorMessage, $validateFrom = self::PJS_VALIDATION) {
			if ($validateFrom == self::PJS_VALIDATION OR $validateFrom == self::PHP_VALIDATION) {
				foreach ($this->_phpValidationsRules as &$rule) {
					if ($rule['rule'] == $ruleName) {
						$rule['errorMessage'] = $errorMessage;
					}
				}
				
				unset($rule);
			}
			
			if ($validateFrom == self::PJS_VALIDATION OR $validateFrom == self::JS_VALIDATION) {
				foreach ($this->_jsValidationsRules as &$rule) {
					if ($rule['rule'] == $ruleName) {
						$rule['errorMessage'] = $errorMessage;
					}
				}
				
				unset($rule);
			}
		}
		
	}
	
?>